package com.sparrow.common.util;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * ReflectionUtil 类提供了基于反射机制的工具方法，用于处理类、注解、字段、方法和构造函数相关的操作。
 */
public class ReflectionUtil {

    /**
     * 获取指定类中带有特定注解的所有字段。
     *
     * @param clazz            要检查的类
     * @param annotationClass  要查找的注解类
     * @return 带有指定注解的字段数组，如果没有找到则返回空数组
     * @throws NullPointerException 如果传入的 clazz 或 annotationClass 为 null
     */
    public static Field[] getFieldsWithAnnotation(Class<?> clazz, Class<? extends Annotation> annotationClass) {
        if (clazz == null || annotationClass == null) {
            throw new NullPointerException("输入的类或注解类不能为 null");
        }
        Field[] allFields = clazz.getDeclaredFields();
        List<Field> resultFields = new ArrayList<>();
        for (Field field : allFields) {
            Annotation annotation = field.getDeclaredAnnotation(annotationClass);
            if (annotation != null) {
                resultFields.add(field);
            }
        }
        return resultFields.toArray(new Field[0]);
    }

    /**
     * 创建指定类的实例，使用无参构造函数。
     *
     * @param clazz 要实例化的类
     * @param <T>   类的类型
     * @return 类的实例
     * @throws NoSuchMethodException     如果类没有无参构造函数
     * @throws IllegalAccessException    如果无法访问构造函数
     * @throws InstantiationException    如果实例化过程中出现问题
     * @throws InvocationTargetException 如果构造函数抛出异常
     */
    public static <T> T createInstance(Class<T> clazz) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
        Constructor<T> constructor = clazz.getDeclaredConstructor();
        constructor.setAccessible(true);
        return constructor.newInstance();
    }

    /**
     * 调用对象的指定方法。
     *
     * @param obj        要调用方法的对象
     * @param methodName 要调用的方法名
     * @param args       方法的参数
     * @return 方法的返回值，如果方法返回类型为 void 则返回 null
     * @throws NoSuchMethodException     如果对象中不存在指定的方法
     * @throws IllegalAccessException    如果无法访问该方法
     * @throws InvocationTargetException 如果方法调用过程中抛出异常
     */
    public static Object invokeMethod(Object obj, String methodName, Object... args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        Class<?>[] argTypes = new Class[args.length];
        for (int i = 0; i < args.length; i++) {
            argTypes[i] = args[i].getClass();
        }
        Method method = obj.getClass().getMethod(methodName, argTypes);
        method.setAccessible(true);
        return method.invoke(obj, args);
    }

    /**
     * 获取对象的指定字段的值。
     *
     * @param obj       要获取字段值的对象
     * @param fieldName 要获取的字段名
     * @return 字段的值
     * @throws NoSuchFieldException   如果对象中不存在指定的字段
     * @throws IllegalAccessException 如果无法访问该字段
     */
    public static Object getFieldValue(Object obj, String fieldName) throws NoSuchFieldException, IllegalAccessException {
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        return field.get(obj);
    }

    /**
     * 设置对象的指定字段的值。
     *
     * @param obj       要设置字段值的对象
     * @param fieldName 要设置的字段名
     * @param value     要设置的值
     * @throws NoSuchFieldException   如果对象中不存在指定的字段
     * @throws IllegalAccessException 如果无法访问该字段
     */
    public static void setFieldValue(Object obj, String fieldName, Object value) throws NoSuchFieldException, IllegalAccessException {
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }
}